package com.sjs.boot.dynamic.core.config.method;

import cn.hutool.core.util.StrUtil;
import com.sjs.boot.dynamic.core.config.method.parameter.CompoundParameterInfo;
import com.sjs.boot.dynamic.core.config.method.process.SelfRDBProcessConfig;
import com.sjs.boot.dynamic.core.config.method.request.JsonRequestConfig;
import com.sjs.boot.dynamic.core.config.method.response.JsonResponseConfig;
import com.sjs.boot.dynamic.core.run.process.SelfRDBProcessor;
import com.sjs.boot.dynamic.core.run.request.JsonRequester;
import com.sjs.boot.dynamic.core.run.response.JsonResponser;
import com.sjs.boot.dynamic.facade.config.AbstractProcessConfig;
import com.sjs.boot.dynamic.facade.config.AbstractRequestConfig;
import com.sjs.boot.dynamic.facade.config.AbstractResponseConfig;
import com.sjs.boot.dynamic.facade.run.AbstractProcessor;
import com.sjs.boot.dynamic.facade.run.AbstractRequester;
import com.sjs.boot.dynamic.facade.run.AbstractResponser;
import com.sjs.boot.dynamic.protocol.Const;
import com.sjs.boot.dynamic.util.ValParser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author uto_dev_008
 * @desc methods配置解析器
 * @since 2021/11/9 18:20
 * Copyright (c) 2021 南京友拓信息技术有限公司 All rights reserved
 */
public class MethodConfig {
    private static final Logger LOG = LoggerFactory.getLogger(MethodConfig.class);


    private static final String CONFIG_NAME = "name";
    private static final String CONFIG_TYPE = "type";

    private static final String CONFIG_WRITELOG = "writeLog";
    private static final String CONFIG_TYPES = "types"; //局部对象定义
    private static final String CONFIG_REQUEST = "request";
    private static final String CONFIG_PROCESS = "process";
    private static final String CONFIG_RESPONSE = "response";

    private static final String CONFIG_RESULTCODENAME = "resultCodeName";
    private static final String CONFIG_REASONNAME = "reasonName";


    private String name = null;
    private String type = null;

    private int maxInterval = Integer.MAX_VALUE;

    private boolean writeLog = false;

    private String resultCodeName = Const.RESULT;
    private String reasonName = Const.REASON;

    private String ver = null;

    private AbstractRequestConfig requestConfig = null;
    private AbstractProcessConfig processConfig = null;
    private AbstractResponseConfig responseConfig = null;

    private AbstractRequester requester;
    private AbstractProcessor processor;
    private AbstractResponser responser;
//    private IMethodAide aide;


    /**
     * 记录所有接口配置相关的限制信息，比如RDB的请求、处理、响应的配置及运行时的处理类，
     * 在InitListener中可以在AbstractInitializer.beforeInit中可以加载自己的接口配置信息
     */
    private static final Map<String, ProcessInfo> PROCESSORS = new HashMap<String, ProcessInfo>();
    private final Map<String, CompoundParameterInfo> types = new HashMap<String, CompoundParameterInfo>();

    public static final void init() {
        /**
         * 以json方式传递参数，只能用post，json串在消息体中，
         */

        addMethodType("SELF-RDB", JsonRequestConfig.class, SelfRDBProcessConfig.class, JsonResponseConfig.class,
                new JsonRequester(), new SelfRDBProcessor(), new JsonResponser());
    }

    /**
     * 解析方法
     *
     * @param version
     * @param json
     * @return
     */
    public static final MethodConfig parse(String version, Map<String, Object> json) {
        String name = ValParser.getAsStr(json, CONFIG_NAME, "").trim().toLowerCase();
        if (StrUtil.isEmpty(name)) {
            LOG.error("Invalid method config, because no config item {}", CONFIG_NAME);
            return null;
        }

        String type = ValParser.getAsStr(json, CONFIG_TYPE, "").trim().toUpperCase();
        if (StrUtil.isEmpty(type)) {
            LOG.error("Invalid method config, because no config item {}, name={}", CONFIG_TYPE, name);
            return null;
        }

        ProcessInfo pi = PROCESSORS.get(type);
        if (pi == null) {
            LOG.error("Invalid method {}, no processor matched", name);
            return null;
        }

        MethodConfig mc = new MethodConfig();
        if (json.containsKey(CONFIG_TYPES)) {
            List<Object> tds = ValParser.getAsList(json, CONFIG_TYPES);
            int num = tds.size();
            for (int i = 0; i < num; i++) {
                CompoundParameterInfo td = CompoundParameterInfo.parse(version, ValParser.getAsObject(tds, i), mc);
                mc.types.put(td.getName(), td);
            }
        }


        mc.name = name;
        mc.type = type;
        mc.ver = version;

        mc.writeLog = ValParser.getAsBool(json, CONFIG_WRITELOG, true);

        mc.resultCodeName = ValParser.getAsStr(json, CONFIG_RESULTCODENAME, mc.resultCodeName);
        mc.reasonName = ValParser.getAsStr(json, CONFIG_REASONNAME, mc.reasonName);

        /**
         * 在request/process/response的配置中都可以设置handler，
         * 来改变默认的处理器
         */
        mc.setRequester(pi.requester);
        mc.setProcessor(pi.processor);
        mc.setResponser(pi.responser);

        try {
            //解析request信息
            mc.requestConfig = pi.requestCfgCls.newInstance();
            if (!mc.requestConfig.parse(version, ValParser.getAsObject(json, CONFIG_REQUEST), mc)) {
                LOG.error("Fail to parse method {}, invalid {}", name, CONFIG_REQUEST);
                return null;
            }

            //先处理resononse信息，因为process中可能会用到response信息
            //而response中不会用到process中信息
            mc.responseConfig = pi.responseCfgCls.newInstance();
            if (!mc.responseConfig.parseConfig(version, ValParser.getAsObject(json, CONFIG_RESPONSE), mc)) {
                LOG.error("Fail to parse method {}, invalid {}", name, CONFIG_RESPONSE);
                return null;
            }

            mc.processConfig = pi.procCfgCls.newInstance();
            if (!mc.processConfig.parseConfig(version, ValParser.getAsObject(json, CONFIG_PROCESS), mc)) {
                LOG.error("Fail to parse method {}, invalid {}", name, CONFIG_PROCESS);
                return null;
            }
        } catch (Exception e) {
            LOG.error("Fail to parse method {}", name, e);
            return null;
        }

        if (!mc.parseExt(json)) {
            return null;
        }
        return mc;
    }

    protected boolean parseExt(Map<String, Object> json) {
        return true;
    }

    public AbstractRequestConfig getRequestConfig() {
        return requestConfig;
    }

    public AbstractProcessConfig getProcessConfig() {
        return processConfig;
    }

    public AbstractResponseConfig getResponseConfig() {
        return responseConfig;
    }


    public String getName() {
        return name;
    }

    /**
     * 类型，通过addMethodType添加，默认的有RDB/PAGE/JAVA/BATCHRDB等
     *
     * @return
     */
    public String getType() {
        return type;
    }

    public CompoundParameterInfo getType(String name) {
        CompoundParameterInfo td = types.get(name); //首先查看局部是否有对象定义
        if (td != null) {
            return td;
        }
        //如果没有，再找全局对象定义
        return Methods.getGlobalType(this.ver, name);
    }
    /**
     * 操作是否需要写日志，默认情况下不处理此配置
     *
     * @return
     */
    public boolean isCanLog() {
        return writeLog;
    }

    public AbstractRequester getRequester() {
        return requester;
    }

    public void setRequester(AbstractRequester requester) {
        this.requester = requester;
    }

    public AbstractProcessor getProcessor() {
        return processor;
    }

    public void setProcessor(AbstractProcessor processor) {
        this.processor = processor;
    }

    public AbstractResponser getResponser() {
        return responser;
    }

    public void setResponser(AbstractResponser responser) {
        this.responser = responser;
    }


    /**
     * 业务正常执行的最大时间间隔，用于判断任务队列中的任务是否需要降级处理
     *
     * @return
     */
    public int getMaxInterval() {
        return maxInterval;
    }

    /**
     * 内部使用，不对外开放
     * 用于记录不同接口请求、处理、响应的不同配置
     */
    private static class ProcessInfo {
        Class<? extends AbstractRequestConfig> requestCfgCls;
        Class<? extends AbstractProcessConfig> procCfgCls;
        Class<? extends AbstractResponseConfig> responseCfgCls;

        //        IMethodAide aide;
        AbstractProcessor processor;
        AbstractRequester requester;
        AbstractResponser responser;

        public ProcessInfo(Class<? extends AbstractRequestConfig> requestCfgCls,
                           Class<? extends AbstractProcessConfig> procCfgCls,
                           Class<? extends AbstractResponseConfig> responseCfgCls,
//                           IMethodAide aide,
                           AbstractRequester requester,
                           AbstractProcessor processor,
                           AbstractResponser responser) {
            this.requestCfgCls = requestCfgCls;
            this.procCfgCls = procCfgCls;
            this.responseCfgCls = responseCfgCls;
//            this.aide = aide;
            this.processor = processor;
            this.requester = requester;
            this.responser = responser;
        }
    }
//
//    public static final boolean addMethodType(
//            String type,
//            Class<? extends AbstractRequestConfig> requestCfgCls,
//            Class<? extends AbstractProcessConfig> procCfgCls,
//            Class<? extends AbstractResponseConfig> responseCfgCls,
//            AbstractRequester requester,
//            AbstractProcessor processor,
//            AbstractResponser responser) {
//        return addMethodType(type, requestCfgCls, procCfgCls, responseCfgCls,
//                WAFConfig.getAide(), requester, processor, responser);
//
//    }

    public static final boolean addMethodType(
            String type,
            Class<? extends AbstractRequestConfig> requestCfgCls,
            Class<? extends AbstractProcessConfig> procCfgCls,
            Class<? extends AbstractResponseConfig> responseCfgCls,
//            IMethodAide aide,
            AbstractRequester requester,
            AbstractProcessor processor,
            AbstractResponser responser) {
        LOG.info("Method Type: {}, \nconfig, request:{},process:{},response:{}\nprocess, requester:{}, processor:{}, responser:{}",
                type, requestCfgCls.getName(), procCfgCls.getName(), responseCfgCls.getName(),
                requester.getClass().getName(), processor.getClass().getName(), responser.getClass().getName());
        type = type.trim().toUpperCase();
        if (PROCESSORS.containsKey(type)) {
            LOG.error("Processor {} already exists", type);
            return false;
        }

        if ((requestCfgCls.getModifiers() & Modifier.ABSTRACT) != 0) {
            LOG.error("Abstract requestCfgCls {}", requestCfgCls.getName());
            return false;
        }

        if ((procCfgCls.getModifiers() & Modifier.ABSTRACT) != 0) {
            LOG.error("Abstract procCfgCls {}", procCfgCls.getName());
            return false;
        }

        if ((responseCfgCls.getModifiers() & Modifier.ABSTRACT) != 0) {
            LOG.error("Abstract responseCfgCls {}", responseCfgCls.getName());
            return false;
        }

        if ((requester.getClass().getModifiers() & Modifier.ABSTRACT) != 0) {
            LOG.error("Abstract requester {}", requester.getClass().getName());
            return false;
        }

        if ((processor.getClass().getModifiers() & Modifier.ABSTRACT) != 0) {
            LOG.error("Abstract processor {}", processor.getClass().getName());
            return false;
        }

        if ((responser.getClass().getModifiers() & Modifier.ABSTRACT) != 0) {
            LOG.error("Abstract responser {}", responser.getClass().getName());
            return false;
        }

        if (!processor.init()) {
            LOG.error("Fail to call {}.init()", processor.getClass().getName());
            return false;
        }
        PROCESSORS.put(type, new ProcessInfo(requestCfgCls, procCfgCls, responseCfgCls, requester, processor, responser));

        return true;
    }


    /**
     * 每个接口可以使用不同的resultCode与reason名称
     *
     * @return
     */
    public String getResultCodeName() {
        return resultCodeName;
    }

    public String getReasonName() {
        return reasonName;
    }

    /**
     * 接口的版本号
     *
     * @return
     */
    public String getVersion() {
        return ver;
    }
}
